{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "ioaprt5q5US7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "yCl0eTNH5RS3"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# Klasifikasi Teks dengan teks yang telah diproses: Review Film"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hKY4XMc9o8iB"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/text_classification\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Lihat diTensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/id/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Jalankan di Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/id/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Lihat source code di GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/id/tutorials/keras/text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Unduh notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Eg62Pmz3o83v"
      },
      "source": [
        "Notebook ini mengklasifikasikan review dari film sebagai *positif* atau *negatif* menggunakan teks dari review tersebut. Ini merupakan contoh dari klasifikasi biner atau klasifikasi dengan dua kelas, sebuah permasalahan machine learning yang penting dan sering ditemukan.\n",
        "\n",
        "Kita akan menggunakan data [IMDB dataset](https://www.tensorflow.org/datasets/catalog/imdb_reviews) yang terdiri atas 50,000 teks review film di [Internet Movie Database](https://www.imdb.com/). Data ini di bagi menjadi 25,000 review untuk proses training dan 25,000 review untuk testing. Data training dan testing sudah *seimbang*, artinya data mengandung jumlah positif dan negatif review yang sama.\n",
        "\n",
        "Notebook ini menggunakan [tf.keras](https://www.tensorflow.org/guide/keras), sebuah API tingkat tinggi untuk membuat model di TensorFlow. Untuk tutorial klasifikasi teks yang lebih kompleks menggunakan `tf.keras`, lihat [MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8vdyFn79gt1L"
      },
      "source": [
        "## Pengaturan"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Nh0KjNGMWNlL"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8RZOuS9LWQvv"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  # %tensorflow_version only exists in Colab.\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2ew7HTbPpCJH"
      },
      "outputs": [],
      "source": [
        "from tensorflow import keras\n",
        "\n",
        "!pip install tensorflow-datasets\n",
        "import tensorflow_datasets as tfds\n",
        "tfds.disable_progress_bar()\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "print(tf.__version__)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iAsKG535pHep"
      },
      "source": [
        "<a id=\"download\"></a>\n",
        "\n",
        "## Unduh dataset IMDB\n",
        "\n",
        "Dataset review film IMDB terdapat dalam package `tfds`. Teks yang terdapat dalam dataset ini sudah diproses terlebih dahulu sehingga review (urutan kata-kata) telah dikonversi menjadi urutan bilangan integer, dimana setiap integer merepresentasikan sebuah kata yang spesifik di dalam *dictionary*.\n",
        "\n",
        "Kode berikut ini mengunduh dataset IMDB ke komputer (atau menggunakan *cached copy* apabila Anda telah mendownloadnya):\n",
        "\n",
        "Untuk proses encode dari text Anda sendiri lihat [Loading text tutorial](../load_data/text.ipynb)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wbIQ2wSeXSme"
      },
      "outputs": [],
      "source": [
        "(train_data, test_data), info = tfds.load(\n",
        "    # Use the version pre-encoded with an ~8k vocabulary.\n",
        "    'imdb_reviews/subwords8k', \n",
        "    # Return the train/test datasets as a tuple.\n",
        "    split = (tfds.Split.TRAIN, tfds.Split.TEST),\n",
        "    # Return (example, label) pairs from the dataset (instead of a dictionary).\n",
        "    as_supervised=True,\n",
        "    # Also return the `info` structure. \n",
        "    with_info=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qvA8HYDJj8OU"
      },
      "source": [
        "<a id=\"encoder\"></a>\n",
        "\n",
        "## Mencoba Encoder\n",
        "\n",
        " `info` dataset ini meliputi enkoder dari teks (a `tfds.features.text.SubwordTextEncoder`)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EplYp5pNnW1S"
      },
      "outputs": [],
      "source": [
        "encoder = info.features['text'].encoder"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "e7ACuHM5hFp3"
      },
      "outputs": [],
      "source": [
        "print ('Vocabulary size: {}'.format(encoder.vocab_size))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tAfGg8YRe6fu"
      },
      "source": [
        "Enkoder teks akan mengubah kembali bilangan integer menjadi string"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Bq6xDmf2SAs-"
      },
      "outputs": [],
      "source": [
        "sample_string = 'Hello TensorFlow.'\n",
        "\n",
        "encoded_string = encoder.encode(sample_string)\n",
        "print ('Encoded string is {}'.format(encoded_string))\n",
        "\n",
        "original_string = encoder.decode(encoded_string)\n",
        "print ('The original string: \"{}\"'.format(original_string))\n",
        "\n",
        "assert original_string == sample_string"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TbhM970AVA8w"
      },
      "source": [
        "Enkoder akan mengubah string dengan memecah string tersebut menjadi beberapa kata atau karakter apabila kata tersebut tidak terdapat di dalam *dictionary*. Jadi apabila string tersebut semakin menyerupai dataset, hasil *encode* dari string tersebut akan semakin singkat."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GUIRWSO8yxT5"
      },
      "outputs": [],
      "source": [
        "for ts in encoded_string:\n",
        "  print ('{} ----> {}'.format(ts, encoder.decode([ts])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l50X3GfjpU4r"
      },
      "source": [
        "## Eksplorasi data\n",
        "\n",
        "Mari mencoba untuk memahami format dari data. Dataset sudah diproses terlebih dahulu sebelumnya: setiap data merupakan array dari integer yang merepresentasikan kata-kata yang terdapat di dalam review film.\n",
        "\n",
        "Teks dari review sudah dikonversi ke integer, dimana setiap integer merepresentasikan sebuah kata spesifik di dalam *dictionary*.\n",
        "\n",
        "Setiap label merupakan integer dengan value antara 0 atau 1, dimana 0 berarti review yang negatif, dan 1 berarti review yang positif.\n",
        "\n",
        "Review pertama terlihat seperti ini:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cxnWQJijdGA1"
      },
      "outputs": [],
      "source": [
        "for train_example, train_label in train_data.take(1):\n",
        "  print('Encoded text:', train_example[:10].numpy())\n",
        "  print('Label:', train_label.numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wy0v9Hs4v41q"
      },
      "source": [
        "struktur `info` memiliki enkoder/dekoder. Enkoder dapat digunakan untuk mengembalikan teks yang asli:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "34VUXtgxsVpf"
      },
      "outputs": [],
      "source": [
        "encoder.decode(train_example)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qJmTiO0IYAjm"
      },
      "source": [
        "## Mempersiapkan data untuk proses training\n",
        "\n",
        "Anda menginginkan untuk membuat beberapa batch data training unutk model Anda. Review memiliki jumlah kata yang berbeda-beda, sehingga gunakan `padded_batch` untuk menambahkan nilai kosong dalam urutan kata ketika membuat batch:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SDRI_s_tX1Hk"
      },
      "outputs": [],
      "source": [
        "BUFFER_SIZE = 1000\n",
        "\n",
        "train_batches = (\n",
        "    train_data\n",
        "    .shuffle(BUFFER_SIZE)\n",
        "    .padded_batch(32, train_data.output_shapes))\n",
        "\n",
        "test_batches = (\n",
        "    test_data\n",
        "    .padded_batch(32, train_data.output_shapes))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9D9pIr0JwvRl"
      },
      "source": [
        "Setiap batch akan memiliki bentuk `(batch_size, sequence_length)` karena padding yang ditambahkan bersifat dinamis sehingga setiap batch akan ditambahkan padding yang berbeda panjangnya:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sXXne4DreQfv"
      },
      "outputs": [],
      "source": [
        "for example_batch, label_batch in train_batches.take(2):\n",
        "  print(\"Batch shape:\", example_batch.shape)\n",
        "  print(\"label shape:\", label_batch.shape)\n",
        "  "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LLC02j2g-llC"
      },
      "source": [
        "## Membuat Model\n",
        "\n",
        "Neural network dibuat dengan menumpuk layer-layer—hal ini membutuhkan dua keputusan arsitektural utama:\n",
        "\n",
        "* Berapa layer yang akan digunakan dalam model?\n",
        "* Berapa banyak *hidden units* yang akan digunakan tiap layer?\n",
        "\n",
        "Dalam kasus ini, data input terdiri atas array berisi indeks dari kata-kata. Label yang akan diprediksi antara 0 atau 1. Mari buat model dengan cara \"*Conitunous bag of words*\" untuk permasalahan ini:\n",
        "\n",
        "Peringatan: Model ini tidak menggunaan *masking*, sehingga *zero-padding* digunakan sebagai bagian dari input, sehingga jumlah padding dapat memengaruhi output dari model. Untuk memperbaiki hal ini, lihat [masking and padding guide](../../guide/keras/masking_and_padding)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xpKOoWgu-llD"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential([\n",
        "  keras.layers.Embedding(encoder.vocab_size, 16),\n",
        "  keras.layers.GlobalAveragePooling1D(),\n",
        "  keras.layers.Dense(1, activation='sigmoid')])\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "Layer-layer yang ditumpuk secara berurutan untuk membuat model klasifikasi:\n",
        "\n",
        "1. Layer yang pertama adalah layer `Embedding`. Layer ini mengambil kosa kata dari integer yang sudah dienkode dan melihat vektor untuk masing-masing indeks kata. Vektor ini dipelajari bersamaan ketika model dilatih. Vektor ini menambahkan dimensi terhadap array output. Dimensi yang dihasilkan adalah: `(batch, sequence, embedding)`.\n",
        "2. Selanjutnya, layer `GlobalAveragePooling1D` mengembalikan sebuah vektor output dengan panjang yang tetap untuk setiap data dengan merata-ratakan urutan dari dimensi. Hal ini menyebabkan model dapat mengatasi input dari panjang variabel dengan cara yang paling sederhana.\n",
        "3. Vektor dengan panjang tetap ini kemudian melewati sebuah layer terhubung seluruhnya (`Dense`), layer ini memiliki 16 *hidden units*.\n",
        "4. Layer terakhir adalah layer padat yang terhubung seluruhnya dengan satu node output. Menggunakan fungsi aktivasi `sigmoid`, nilai ini adalah bertipe float antara 0 dan 1, merepresentasikan probabilitas, atau tingkat percaya diri."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0XMwnDOp-llH"
      },
      "source": [
        "### Hidden units\n",
        "\n",
        "Model diatas memiliki dua layer intermediate atau \"hidden\" layer, antara input dengan output. Jumlah output (unit, node, atau neuron) merupakan dimensi hasil representasi layer-layer tersebut. Dengan kata lain, kebebasan untuk menentukan jumlah network diperbolehkan ketika mempelajari representasi internal. \n",
        "\n",
        "Jika sebuah model memiliki hidden units lebih banyak (ruang dimensional representasi yang lebih tinggi), dan/atau memiliki jumlah layer lebih banyak, maka model tersebut dapat mempelajari representasi yang lebih kompleks. Akan tetapi, hal ini akan membuat model neural network menjadi lama dalam proses komputasi dan dapat menyebabkan model akan mempelajari pattern yang meningkatkan performansi pada data training tetapi tidak pada data test. Hal ini disebut dengan *overfitting*, dan kita akan menelusurinya lebih jauh."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### Loss function and optimizer\n",
        "\n",
        "Sebuah model memerlukan *loss function* dan sebuah optimizer ketika proses training. Contoh ini merupakan kasus klasifikasi biner dan model menghasilkan probabilitas (sebuah layer dengan satu neuron menggunakan aktivasi sigmoid), kita akan menggunakan *loss function* berupa `binary_crossentropy`.\n",
        "\n",
        "Ini bukanlah satu-satunya pilihan untuk *loss function*, Anda dapat, misalnya, memilih `mean_squared_error`. Akan tetapi, biasanya, `binary_crossentropy` lebih baik untuk mengatasi probabilitas—fungsi ini menghitung \"jarak\" antara distribusi probabilitas, atau dalam kasus ini, antara distribusi yang sebenarnya dengan prediksi.\n",
        "\n",
        "Nanntinya, ketika kita menemui kasus regresi (misalnya, untuk memprediksi harga rumah), kita akan melihat bagaimana caranya menggunakan *loss function* lainnya yang disebut *mean squarred error*.\n",
        "\n",
        "Sekarang, konfigurasi model menggunakan optimizer dan *loss function*:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Mr0GP-cQ-llN"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss='binary_crossentropy',\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "35jv_fzP-llU"
      },
      "source": [
        "## Melakukan training terhadap model\n",
        "\n",
        "Model dilatih dengan memasukan objek `Dataset` ke fungsi fit dari model. Kemudian jumlah epochs diatur."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tXSGrjWZ-llW"
      },
      "outputs": [],
      "source": [
        "history = model.fit(train_batches,\n",
        "                    epochs=10,\n",
        "                    validation_data=test_batches,\n",
        "                    validation_steps=30)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "## Model Evaluasi\n",
        "\n",
        "Dan mari lihat bagaimana performansi dari model. Dua nilai akan dihasilkan. Loss (nilai yang merepresentasikan eror, semakin rendah nilainya semakin baik), dan akurasi."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zOMKywn4zReN"
      },
      "outputs": [],
      "source": [
        "loss, accuracy = model.evaluate(test_batches)\n",
        "\n",
        "print(\"Loss: \", loss)\n",
        "print(\"Accuracy: \", accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "Pendekatan yang cukup sederhana ini memperoleh akurasi sekitar 87%. Dengan cara yang lebih kompleks, model seharusya memperoleh akurasi mendekati 95%."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5KggXVeL-llZ"
      },
      "source": [
        "## Membuat grafik akurasi dan loss tiap waktu\n",
        "\n",
        "`model.fit()` menghasilkan sebuah objek `History` yang mengandung sebuah *dictionary* dengan seluruh aktivitas yang terjadi ketika proses training berlangsung:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VcvSXvhp-llb"
      },
      "outputs": [],
      "source": [
        "history_dict = history.history\n",
        "history_dict.keys()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nRKsqL40-lle"
      },
      "source": [
        "Terdapat empat buah nilai: masing-masing satu nilai metriks (loss dan akurasi) yang terpantau ketika proses training dan validasi. Kita akan menggunakan nilai ini untuk membuat plot yang membandingkan antara nilai *loss* dari training dan validasi, juga plot yang membandingkan antara nilai akurasi training dan validasi:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nGoYf2Js-lle"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "acc = history_dict['accuracy']\n",
        "val_acc = history_dict['val_accuracy']\n",
        "loss = history_dict['loss']\n",
        "val_loss = history_dict['val_loss']\n",
        "\n",
        "epochs = range(1, len(acc) + 1)\n",
        "\n",
        "# \"bo\" is for \"blue dot\"\n",
        "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
        "# b is for \"solid blue line\"\n",
        "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
        "plt.title('Training and validation loss')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6hXx-xOv-llh"
      },
      "outputs": [],
      "source": [
        "plt.clf()   # clear figure\n",
        "\n",
        "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
        "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
        "plt.title('Training and validation accuracy')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oFEmZ5zq-llk"
      },
      "source": [
        "Dalam plot ini, titik merepresentasikan loss dan akurasi dari training, dan garis solid untuk loss dan akurasi dari validasi.\n",
        "\n",
        "Perhatikan bahwa loss dari training *menurun* tiap epoch dan akurasinya *meningkat* tiap epoch. Hal ini yang kita kehendaki ketika menggunakan sebuah *gradient descent optimization*—optimisasi ini seharusnya meminimalkan jumlah yang diinginkan pada setiap iterasi.\n",
        "\n",
        "Hal ini tidak terjadi untuk loss dan akurasi dari validasi—mereka terlihat mencapai titik tertinggi setelah dua puluh epoch. Hal ini merupakan contoh dari *overfitting*: model memiliki performansi yang lebih baik pada data training dibandingkan dengan performansinya terhadap data yang belum pernah dilihat sebelumnya. Setelah titik ini, model terlalu optimis dan mempelajari representasi yang sangat *spesifik* dari data train yang tidak *menggambarkan* data test.\n",
        "\n",
        "Untuk kasus semacam ini, kita dapat mencegah *overfitting* dengan memberhentikan proses training setelah sekitar dua puluh epoch. Nantinya, Anda akan melihat bagaimana caranya melakukan hal ini secara otomatis menggunakan sebuah *callback*."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "text_classification.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
